#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <unistd.h>
#include "socket.hpp"
#include "log_pro.hpp"
#include "epoller.hpp"
#include "nocopy.hpp"
const int defaultPort = 10000;
const int maxevent = 64; // 一次可以获取的就绪队列长度

class epollServer : public nocopy // 继承禁止拷贝的类
{
public:
    epollServer(int port = defaultPort)
        : _port(port), _epoll_ptr(new epoller()), _listensock_ptr(new Socket())
    {
        int opt = 1;
        setsockopt(_listensock_ptr->getsock(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
    }

    void toAccept()
    {
        std::string clientIp;
        uint16_t clientPort;
        int fd = _listensock_ptr->Accept(clientIp, &clientPort);
        // 接收成功，将新的文件描述符设置到epoll模型中
        _epoll_ptr->ctl(EPOLL_CTL_ADD, fd, EPOLLIN);
        lg(NOTICE, "get new link %s:%d", clientIp.c_str(), clientPort); // 输出一下获取到新连接
    }

    void toResolve(int pos) // pos是就绪文件在event数组中的位置
    {
        char buffer[1024];
        int n = read(_ev[pos].data.fd, buffer, sizeof(buffer) - 1);
        if (n < 0)
        {
            lg(ERROR, "read fail %d: %s", errno, strerror(errno));
            _epoll_ptr->ctl(EPOLL_CTL_DEL, _ev[pos].data.fd, 0); // 因为是移除所以不关系第三个参数是什么事件
            close(_ev[pos].data.fd);
        }
        else if (n == 0)
        {
            lg(NOTICE, "client quit!");
            _epoll_ptr->ctl(EPOLL_CTL_DEL, _ev[pos].data.fd, 0); // 因为是移除所以不关系第三个参数是什么事件
            close(_ev[pos].data.fd);
        }
        else
        { // 正常读情况
            buffer[n - 2] = 0;
            std::string str("echo: ");
            str += buffer;
            std::cout << str << std::endl;
            str += '\n';
            write(_ev[pos].data.fd, str.c_str(), str.size()); // 回写给客户端
        }
    }

    void distribute(int num) // 就绪文件fd个数
    {
        for (int i = 0; i < num; i++)
        {
            if (_ev[i].data.fd == _listensock_ptr->getsock()) // 如果是listenfd就交给accpter
            {
                toAccept();
            }
            else // 其他的则为客户端fd
            {
                toResolve(i);
            }
        }
    }

    void start()
    {
        //_listensock_ptr->debug();
        _epoll_ptr->ctl(EPOLL_CTL_ADD, _listensock_ptr->getsock(), EPOLLIN); // 将listen套接字设置到epoll模型中
        while (true)
        {
            int n = _epoll_ptr->wait(_ev, maxevent); // 开始等待就绪
            if (n == -1)
            {
                lg(ERROR, "epoll_wait fail %d: %s", errno, strerror(errno));
                exit(-1);
            }
            else if (n == 0)
            {
                lg(NOTICE, "文件未就绪");
                continue;
            }
            else
            {
                distribute(n); // 对就绪文件进行操作
            }
        }
    }

    ~epollServer()
    {
        _listensock_ptr->Close(); // 关闭服务器fd
    }

private:
    std::unique_ptr<epoller> _epoll_ptr;
    std::unique_ptr<Socket> _listensock_ptr;
    uint16_t _port;
    struct epoll_event _ev[maxevent];
};